package oss


import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	uuid "github.com/satori/go.uuid"
	"hash"
	"io"
	"mime/multipart"
	"os"
	"path"
	"strings"

	//"order_api/conf"
	//"order_api/global"
	"time"
)

var expire_time int64 = 3000


const (
	base64Table = "12qazxsw34edqqqcvfr56dddddeeeeetgbgggggnhy78ujmkli123456o9ddffdp"
	//UploadDir = "school"
	ApiSecret="9yfX4P5D3iXsljumHK9GietUSSY0v3"
	CallBackUrl=""
	ApiKey="LTAI5tR1MjfzfHkGLivzTXhW"
	Host="https://jmc1.oss-cn-chengdu.aliyuncs.com"
)

//var coder = base64.NewEncoding(base64Table)

//func base64Encode(src []byte) []byte {
	//	return []byte(coder.EncodeToString(src))
	//}

func get_gmt_iso8601(expire_end int64) string {
	var tokenExpire = time.Unix(expire_end, 0).Format("2006-01-02T15:04:05Z")
	return tokenExpire
}

type ConfigStruct struct {
	Expiration string     `json:"expiration"`
	Conditions [][]string `json:"conditions"`
}

type PolicyToken struct {
	AccessKeyId string `json:"accessid"`
	Host        string `json:"host"`
	Expire      int64  `json:"expire"`
	Signature   string `json:"signature"`
	Policy      string `json:"policy"`
	Directory   string `json:"dir"`
	Callback    string `json:"callback"`
}

type CallbackParam struct {
	CallbackUrl      string `json:"callbackUrl"`
	CallbackBody     string `json:"callbackBody"`
	CallbackBodyType string `json:"callbackBodyType"`
}

func GetPolicyToken(UploadDir string) *PolicyToken {
	now := time.Now().Unix()
	expire_end := now + expire_time
	var tokenExpire = get_gmt_iso8601(expire_end)

	//create post policy json
	var config ConfigStruct
	config.Expiration = tokenExpire
	var condition []string
	condition = append(condition, "starts-with")
	condition = append(condition, "$key")
	condition = append(condition, UploadDir)
	config.Conditions = append(config.Conditions, condition)

	//calucate signature
	result, err := json.Marshal(config)
	debyte := base64.StdEncoding.EncodeToString(result)
	h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(ApiSecret))
	io.WriteString(h, debyte)
	signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))

	var callbackParam CallbackParam
	callbackParam.CallbackUrl = CallBackUrl
	callbackParam.CallbackBody = "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}"
	callbackParam.CallbackBodyType = "application/x-www-form-urlencoded"
	callback_str, err := json.Marshal(callbackParam)
	if err != nil {
		//global.Logger.Error("oss_err:", zap.String("callback json err:", err.Error()))
	}
	callbackBase64 := base64.StdEncoding.EncodeToString(callback_str)

	var policyToken PolicyToken
	policyToken.AccessKeyId = ApiKey
	policyToken.Host = Host
	policyToken.Expire = expire_end
	policyToken.Signature = string(signedStr)
	policyToken.Directory = UploadDir
	policyToken.Policy = string(debyte)
	policyToken.Callback = string(callbackBase64)
	//response, err := json.Marshal(policyToken)
	//if err != nil {
	//	//global.Logger.Error("oss_err:", zap.String("err", err.Error()))
	//}
	return &policyToken
}

// region TODO: 配置oss参数 Author:LiuYanLin Date:2023/03/20 周一 17:44
var (
	aPath             string
	aBucket           string
	aEndpoint         string
	aAccessKeyID      string
	aSecretAccessKey  string
	aStorageClassType string
	aFolder           string
)

func init() {
	aPath = "https://jmc1.oss-cn-chengdu.aliyuncs.com" //存储域名 例如 https://lkasfile.oss-cn-chengdu.aliyuncs.com
	aBucket = "jmc1"                                   //存储桶名称 例如 lkasfile
	aEndpoint = "https://oss-cn-chengdu.aliyuncs.com"      //地域节点 例如 https://oss-cn-chengdu.aliyuncs.com
	aAccessKeyID = "LTAI5tR1MjfzfHkGLivzTXhW"              //云账号 AccessKey 是您访问阿里云 API 的密钥 例如  LTAI5tBTdUZaDDaSHvgz8C97
	aSecretAccessKey = "9yfX4P5D3iXsljumHK9GietUSSY0v3"    //类似于密码 例如 PCqpeigqWivlh2MuI27fdo5ykRzEiU
	aStorageClassType = "Standard"                         //存储桶存储类型 例如  Standard
	//aFolder = "school"                                        //储存目录 例如 kls
}

//endregion

// region TODO: 本地文件普通上传到oss Author:LiuYanLin Date:2023/03/16 周四 16:43

func UploadOss(fileName string) (ossPath string, err error) {

	nameSplit := strings.Split(fileName, "/")

	// 读取文件
	f, err := os.Open(fileName)
	if err != nil {
		return
	}

	// 创建OSSClient实例。
	client, err := oss.New(aEndpoint, aAccessKeyID, aSecretAccessKey, oss.Timeout(10, 120))
	if err != nil {
		return
	}

	// 获取存储空间。
	bucket, err := client.Bucket(aBucket)
	if err != nil {
		return
	}

	// 根据配置文件进行指定存储类型
	var storageType oss.Option
	switch aStorageClassType {
	case "Standard": // 指定存储类型为标准存储，缺省也为标准存储。
		storageType = oss.ObjectStorageClass(oss.StorageStandard)
	case "IA": // 指定存储类型为很少访问存储
		storageType = oss.ObjectStorageClass(oss.StorageIA)
	case "Archive": // 指定存储类型为归档存储。
		storageType = oss.ObjectStorageClass(oss.StorageArchive)
	case "ColdArchive": // 指定存储类型为归档存储。
		storageType = oss.ObjectStorageClass(oss.StorageColdArchive)
	default:
		storageType = oss.ObjectStorageClass(oss.StorageStandard)
	}

	// excel类型文件
	objectType := oss.ContentType("excel/xlsx")

	// 指定访问权限为公共读，缺省为继承bucket的权限。
	objectAcl := oss.ObjectACL(oss.ACLPublicRead)

	// 文件对象名
	objectName := aFolder + "/" + nameSplit[1]

	// 上传
	err = bucket.PutObject(objectName, f, storageType, objectType, objectAcl)
	if err != nil {
		return
	}

	return aPath + "/" + objectName, err
}

//endregion

// region TODO: 普通上传到oss Author:LiuYanLin Date:2023/03/16 周四 16:43

func UploadOss1(file *multipart.FileHeader) (ossPath string, err error) {

	// 创建OSSClient实例。
	client, err := oss.New(aEndpoint, aAccessKeyID, aSecretAccessKey, oss.Timeout(10, 120))
	if err != nil {
		return
	}

	// 获取存储空间。
	bucket, err := client.Bucket(aBucket)
	if err != nil {
		return
	}

	// 根据配置文件进行指定存储类型
	var storageType oss.Option
	switch aStorageClassType {
	case "Standard": // 指定存储类型为标准存储，缺省也为标准存储。
		storageType = oss.ObjectStorageClass(oss.StorageStandard)
	case "IA": // 指定存储类型为很少访问存储
		storageType = oss.ObjectStorageClass(oss.StorageIA)
	case "Archive": // 指定存储类型为归档存储。
		storageType = oss.ObjectStorageClass(oss.StorageArchive)
	case "ColdArchive": // 指定存储类型为归档存储。
		storageType = oss.ObjectStorageClass(oss.StorageColdArchive)
	default:
		storageType = oss.ObjectStorageClass(oss.StorageStandard)
	}

	//获取文件的后缀名
	ext := path.Ext(file.Filename)

	//文件唯一值
	md5 := fmt.Sprintf("%s", uuid.NewV4())

	// 文件对象名
	objectName := aFolder + "/" + md5 + ext

	// 文件类型
	objectType := oss.ContentType("upload/" + ext[1:])

	// 指定访问权限为公共读，缺省为继承bucket的权限。
	objectAcl := oss.ObjectACL(oss.ACLPublicRead)

	// 读取文件
	f, err := file.Open()
	if err != nil {
		return
	}

	// 上传
	err = bucket.PutObject(objectName, f, storageType, objectType, objectAcl)
	if err != nil {
		return
	}

	return aPath + "/" + objectName, err
}

//endregion
